package mop.model.resource;

import java.sql.Timestamp;
import java.util.LinkedList;
import java.util.List;

import mop.exception.IllegalDescriptionException;
import mop.exception.IllegalDuplicationException;
import mop.exception.IllegalIdException;
import mop.exception.IllegalReservationException;
import mop.exception.UnavailableResourceException;
import mop.model.AbstractModel;
import mop.model.AbstractModelWithDescription;
import mop.model.DataBase;
import mop.model.ModelInterface;
import mop.model.task.Task;
import mop.model.task.TaskInterface;


/**
 * Abstract class Resource.
 * 
 * @invar 	description is valid
 *  		|hasValidDescription()
 *  
 * @invar has no double reservations
 * 			|for(Reservation r1 : reservations)
 * 			| for(Reservation r2 : reservations)
 * 			|  if(!r1.equals(r2)) !r1.isDuring(r2)
 * 
 * @author	MOP Team 7
 */
public  class Resource extends AbstractModelWithDescription implements ResourceInterface
{
	/**
	 * Resource Constructor
	 * 
	 * @param 	rt 	resource type
	 * @param	resourceDatabase
	 * 			DataBase object containing all resources.
	 * @param 	description
	 * 			The description of this object.
	 * @effect	Sets the description.
	 * 			| this.setDescription(description)
	 * @post	Resource database reference is set.
	 * @post	propertyChangeSupport is set.
	 * @post	reservations is set to an empty list.
	 * @post	list of tasks requiring this resource is set to an empty list.
	 * @throws 	IllegalDescriptionException 
	 * 			Thrown when the given description is invalid.
	 * @throws	IllegalDuplicationException
	 * 			Thrown when a resource with the given description already exists. 
	 */
	public Resource(ResourceType rt,DataBase<Resource> resourceDatabase, String description) 
		throws IllegalDescriptionException, IllegalDuplicationException
	{
		super(description);
		this.resourceDatabase = resourceDatabase;
		this.rt = rt;
		
		// Add self to database.
		this.resourceDatabase.add(this);
				
		this.reservations = new LinkedList<Reservation>();
		this.tasksRequiring = new LinkedList<Task>();
	}
	
	/***************************************************************************
	 * 						Model interface
	 ***************************************************************************/
	
	/**
	 * @see ModelInterface#getId()
	 */
	public String getId() 
	{
		return toString();
	}
	
	/**
	 * get a reference to the interface
	 * @return this
	 */
	public ResourceInterface getInterface()
	{
		return this;
	}
	
	/***************************************************************************
	 * 							Project database
	 ***************************************************************************/
	
	private DataBase<Resource> resourceDatabase;
	
	/***************************************************************************
	 * 							resource description
	 ***************************************************************************/
	
	 
	 /**
	  * Checks whether a resource with the given description already exists in our database.
	  * 
	  * @param 		String description
	  * 			Resource description
	  * @return		True if a resource with the given description already exists in our database,
	  * 			otherwise false of course.
	  */
	 public boolean isDuplicate(String description)
	 {
		 for(Resource r : resourceDatabase.fetchAll())
		 {
			 if(r.getDescription().equals(description))
				 return true;
		 }
		 return false;
	 }
	
	 /**
	  * Sets the description.
	  * 
	  * @param 		description
	  *  			The description of the resource.
	  * @post 		The description is set.
	  * @throws 	IllegalDescriptionException 
	  *       		if the description isn't ok
	  *       		| !isValidDescription(description)
	  * @throws 	IllegalDuplicationException 
	  * 			Thrown when a resource with the given description already exists.
	  * 			| isDuplicate(description)
	  */
	 private void setDescription(String description) 
	 	throws IllegalDescriptionException, IllegalDuplicationException
	 {
		 if(!isValidDescription(description))
			 throw new IllegalDescriptionException(description);
		 
		 if(isDuplicate(description))
			 throw new IllegalDuplicationException(description);
		 
		 this.description = description;
	 }
	
		
	/***************************************************************************
	 * 					  Tasks requiring this resource
	 ***************************************************************************/
	
	/**
	 * Returns a list of tasks requiring this resource.
	 * 
	 * @return A list of tasks requiring this resource.
	 */
	public List<Task> getTasksRequiring()
	{
		return this.tasksRequiring;
	}
	
	/**
	 * Returns a copy of the list of tasks requiring this resource.
	 * 
	 * @return 	A copy of the list of tasks requiring this resource.
	 */
	public List<TaskInterface> getTasksRequiringList()
	{
		List<TaskInterface> t = new LinkedList<TaskInterface>();
		t.addAll(tasksRequiring);
		return t;
	}
	
	/** A list of tasks requiring this resource. */
	private List<Task> tasksRequiring;
	
	
	/***************************************************************************
	 * 						     Reservation
	 ***************************************************************************/
	
	/**
	 * Adds a reservation for this resource.
	 * 
	 * @param 	start
	 * 			The start time as a Timestamp.
	 * @param	end
	 * 			The end time as a Timestamp.
	 * 
	 * @post	A reservation for the resource is made (added to reservations list).
	 * 			| reservations.contains( new Reservation(start, end) )
	 * 
	 * @throws	IllegalReservationException
	 * 			If the start time is greater than the end time.
	 * @throws	UnavailableResourceException 
	 * 			If the resource is unavailable at that time.
	 */
	public void addReservation(Timestamp start, Timestamp end) 
		throws IllegalReservationException, UnavailableResourceException
	{
		Reservation r = new Reservation(start, end);
		
		if(this.isAvailable(start, end))
			this.reservations.add(r);
		else
			throw new UnavailableResourceException(this.getDescription());
	}
	
	/**
	 * Returns a list of reservations for this Resource.
	 * 
	 * @return	A list of Reservation objects.
	 */
	public List<Reservation> getReservations()
	{
		return this.reservations;
	}
	
	/**
	 * Returns a copy of the list of reservations for this Resource.
	 * 
	 * @return	A copy of the list of Reservation objects.
	 */
	public List<ReservationInterface> getReservationsList()
	{
		List<ReservationInterface> r = new LinkedList<ReservationInterface>();
		r.addAll(reservations);
		return r;
	}
	
	/**
	 * Checks whether this Resource is available or not.
	 * A Resource is available if the current time isn't between 
	 * the start and end of a reservation for that Resource.
	 * 
	 * @return	True if the Resource is available, otherwise false.
	 */
	public boolean isAvailable()
	{
		return this.isAvailable( new Timestamp(System.currentTimeMillis()) );
	}
	
	/**
	 * Checks whether this Resource is available at the given timePeriod or not.
	 * A Resource is available if the give timePeriod isn't between 
	 * the start and end of a reservation for that Resource.
	 * 
	 * @param	timePeriod
	 * 			A time stamp.
	 * 
	 * @pre		timePeriod must be initialized.
	 * 
	 * @return	True if the Resource is available, otherwise false.
	 */
	public boolean isAvailable(Timestamp timePeriod)
	{
		for(Reservation r : this.getReservations())
			if(r.isDuring(timePeriod))
				return false;
		
		return true;
	}
	
	/**
	 * Checks whether this Resource can be reserved between a given time period or not.
	 * 
	 * @param	start
	 * 			A time stamp.
	 * @param	end
	 * 			A time stamp.
	 * 
	 * @pre		start must be initialized.
	 * @pre		end must be initialized.
	 * @pre		end must be AFTER start!
	 * 
	 * @return	True if the Resource is available, otherwise false.
	 */
	public boolean isAvailable(Timestamp start, Timestamp end)
	{
		for(Reservation r : this.getReservations())
			if( r.isDuring(start, end) )
				return false;
		
		return true;
	}
	
	/**
	 * List of reservations of this resource.
	 */
	private LinkedList<Reservation> reservations;
	
	/***************************************************************************
	 * 						       Removal
	 ***************************************************************************/
	
	/**
	 * Checks if this Resource object can be removed.
	 * If it's not required by a task, it can be removed.
	 * 
	 * @return	True if there are no tasks requiring this resource; 
	 * 			otherwise false.
	 */
	public boolean canRemove() 
	{
		return this.getTasksRequiring().size() == 0;
	}
	
	
	/***************************************************************************
	 * 						Resource Type
	 ***************************************************************************/
	
	/**
	 * Get the interface to the current Resource Type
	 * 
	 * @return interface to resource type
	 */
	public ResourceTypeInterface getResourceType()
	{
		return rt.getInterface();
	}
	
	/** the type of the resource */
	private ResourceType rt = null;
	
	/***************************************************************************
	 * 						Java object interface
	 ***************************************************************************/
	
	/**
	 * @see Object#equals(Object)
	 */
	@Override
	public boolean equals(Object obj)
	{
		if(obj.getClass() == getClass())
		{
			if(((Resource) obj).getId().equals(this.getId()))
				return true;
		}
		return false;
	}
	
	/**
	 * @see Object#toString()
	 */
	@Override
	public String toString()
	{
		return getDescription();
	}
	
}